"""
Example streaming server.  This was ripped out of an active project.
Two ports are opened.
11921- Audio Receiver listens for connections, and if it receives one, it
makes a new directory with the first line of data received, and then everything
else is assumed to be ILBC-20 data. Converts the data to MP3, sends it down the chain
    and also over to the streamer

11922- Audio Streamer listens for connections.  Tries to parse out the stream id
you are looking for, and registers you to get any data on that stream.

It does depend on py_ilbc and py_ffmpeg.  You can get those here:
http://code.google.com/u/@VRhfQ1BQBxBBWwV1/

This code is horribly documented because the project was canceled before
completion.  If you have any questions please let me know.  If I have any free
time I am planning on going back and cleaning up to make it more useful.
"""
import os
from ap_streamer.core import Element, Segmenter, ApMediaError, FileWriter
from ap_streamer.encoders import MP3Encoder, ILBCDecoder
from ap_streamer.HTTPStream import Indexer
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.application import internet, service
from datetime import datetime

ONE_MP3_SECOND = 64000 / 8 #64000 kbs / 8 bits per byte
ONE_ILBC_SECOND = 50 * 38 #bytes

DEBUG = True


class CopyAfterFinish(FileWriter):
    """Subclass of FlieWriter to handle the rename on completion"""

    def data_received(self, data=''):
        try:
            FileWriter.data_received(self, data)
        except ApMediaError:
            pass

    def finish(self):
        if self.file:
            self.file.close()
            os.rename(self.file.name, '%s.mp3' % self.file.name)
        self.finish_connected()


class AudioReceiver(LineReceiver):
    stream_id = ''

    def __init__(self):

        self.indexer = Indexer(active_limit=0, segment_name="segment_num%s.ts", target_duration=5,
                               delete_inactive_segments=False)
        self.pre_indexer = Segmenter(bytes=ONE_MP3_SECOND*5)
        self.encoder = MP3Encoder()
        self.decoder = ILBCDecoder()
        self.initialbuffer = Segmenter(bytes=ONE_ILBC_SECOND)
        self.out_to_service = Element(function=self.split_audio_out)
        self.create_mp3 = CopyAfterFinish(append=True)

        #connect all of the pieces
        self.initialbuffer.connect(self.decoder)
        self.decoder.connect(self.encoder)
        self.encoder.connect(self.pre_indexer)
        self.encoder.connect(self.out_to_service)
        self.encoder.connect(self.create_mp3)
        self.pre_indexer.connect(self.indexer)

    def lineLengthExceeded(self, line):
        print "Line length exceeded"
        self.transport.loseConnection()

    def do_debug(self):
        self.decoder_fw = FileWriter(path='debugging/%s/decode/' % self.stream_id,
                                     chunks_per_file = 1)
        self.encoder_fw = FileWriter(path='debugging/%s/encode/' % self.stream_id,
                                     chunks_per_file = 1)
        self.initialbuffer_fw = FileWriter(path='debugging/%s/initialbuf/' %
                                           self.stream_id, chunks_per_file = 1)
        self.whole_ilbc = FileWriter(path='debugging/%s/whole.lbc' %
                                    self.stream_id, append=True)
        self.whole_pcm = FileWriter(path='debugging/%s/whole.pcm' %
                                    self.stream_id, append=True)
        self.whole_mp3 = FileWriter(path='debugging/%s/whole.mp3' %
                                    self.stream_id, append=True)

        self.initialbuffer.connect(self.initialbuffer_fw)
        self.initialbuffer.connect(self.whole_ilbc)
        self.decoder.connect(self.decoder_fw)
        self.decoder.connect(self.whole_pcm)
        self.encoder.connect(self.encoder_fw)
        self.encoder.connect(self.whole_mp3)
        print "Debugging setup"

    def lineReceived(self, data):
        self.stream_id = data.strip()
        try:
            if not os.path.exists(self.stream_id):
                os.mkdir(self.stream_id)
            self.indexer.set_index_file_path('%s/index' % self.stream_id)
            self.create_mp3.set_path('%s' % self.stream_id)
            self.setRawMode()
            if DEBUG:
                self.do_debug()
        except TypeError, e:
            print e
            self.transport.loseConnection()

    def connectionMade(self):
        print 'Connected to client.'

    def connectionLost(self, reason):
        print "Connection Lost"
        try:
            self.initialbuffer.finish()
        except ApMediaError:
            pass

    def rawDataReceived(self, data):
        self.initialbuffer.data_received(data)

    def split_audio_out(self, data):
        self.send_data(self.stream_id, data)

class AudioStreamer(LineReceiver):
    """
    This twisted protocol listens for a connection like
    hostname:11921/stream_id.mp3 from a browser.
    It then sends a header that identifies it as streaming mp3.
    Then registeres itself with the factory to receive new data from the upload
    part fo the streamer.
    """
    stream_id = ''
    index = 0
    connection = False

    def __init__(self):
        self.empty_lines = 0

    def data_received(self, data):
        self.transport.write(data)

    def connectionMade(self):
        print "Connection Made"
        self.connection = True

    def lineReceived(self, data):
        if data.partition(' ')[0] == 'GET':
            self.stream_id = data[data.find('/')+1:data.find('.mp3')]
        if data == '':
            self.empty_lines += 1
        print self.empty_lines, "[%s]" % data
        if self.empty_lines == 1:
            self.send_header()
            if self.stream_id:
                self.factory.start_listening(self)

    def send_header(self):
        self.sendLine("HTTP/1.0 200 OK")
        self.sendLine("Date: %s" % datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S UTC'))
        self.sendLine("Cache-Control: no-cache")
        self.sendLine("Connection: close")
        self.sendLine("")
        self.sendLine("ICY 200 OK")
        self.sendLine("icy-name:Example stream")
        self.sendLine("content-type:audio/mpeg")
        self.sendLine("icy-pub:1")
        self.sendLine("icy-br:8")
        self.sendLine("")

    def connectionLost(self, reason):
        self.connection = False
        if self.stream_id in self.factory.listeners:
            self.factory.listeners[self.stream_id].remove(self)
        print "Connection Lost  %s" % repr(self)

class AudioService(service.Service):

    def __init__(self):
        self.audio_streaming_factory = AudioStreamingFactory()

    def getAudioReceiverFactory(self):
        factory = Factory()
        factory.protocol = AudioReceiver
        factory.protocol.send_data = self.audio_streaming_factory.data_received
        return factory

class AudioStreamingFactory(Factory):
    """Handles the outbound stream.  Basically keeps a buffer"""
    protocol = AudioStreamer

    def __init__(self, buffer_size_in_bytes=50000):
        self.listeners = {}
        self.buffer = ''
        self.buffer_max = buffer_size_in_bytes

    def data_received(self, stream_id, data):
        """
        On new data (from the upload part of the server) update the buffer and
        send the new data to all registered listeners.
        """
        self.buffer += data
        if len(self.buffer) > self.buffer_max:
            self.buffer = self.buffer[-self.buffer_max:]
        if stream_id in self.listeners:
            for listener in self.listeners[stream_id]:
                listener.data_received(data)

    def start_listening(self, instance):
        """When a connection starts listening, send them the buffer then put
        them on the 'List'
        """
        instance.data_received(self.buffer)
        if instance.stream_id in self.listeners:
            self.listeners[instance.stream_id].append(instance)
        else:
            self.listeners[instance.stream_id] = [instance]


mainService = AudioService()
# application object
application = service.Application("Demo application")
serviceCollection = service.IServiceCollection(application)
internet.TCPServer(11921, mainService.getAudioReceiverFactory()).setServiceParent(serviceCollection)
internet.TCPServer(11922, mainService.audio_streaming_factory).setServiceParent(serviceCollection)

